<?php
namespace WebIM;
use Swoole;
use Swoole\Filter;
use WebIM\Store\Mysql;
use WebIM\Store\Redis;
use WebIM\Store\Word;

class Server extends Swoole\Protocol\CometServer
{
    /**
     * @var Store\File;
     */
    protected $store;
    protected $users;

    const MESSAGE_MAX_LEN     = 1024; //单条消息不得超过1K
    const WORKER_HISTORY_ID   = 0;

    function __construct($config = array())
    {

        session_start();
        //检测日志目录是否存在
        $log_dir = dirname($config['webim']['log_file']);
        if (!is_dir($log_dir))
        {
            mkdir($log_dir, 0777, true);
        }
        if (!empty($config['webim']['log_file']))
        {
            $logger = new Swoole\Log\FileLog($config['webim']['log_file']);
        }
        else
        {
            $logger = new Swoole\Log\EchoLog;
        }
        $this->setLogger($logger);   //Logger

        /**
         * 使用文件或redis存储聊天信息
         */
        $this->store = new Redis(); //redis存储
        $this->origin = $config['server']['origin'];
        parent::__construct($config);
    }

    /**
     * 下线时，通知所有人
     */
    function onExit($client_id)
    {
        $this->store->logout($client_id);
        $this->log("onOffline: " . $client_id);
    }

    function onTask($serv, $task_id, $from_id, $data)
    {
        $req = unserialize($data);
        $uid = $this->store->getClientUid($req['fd']);
        file_put_contents('/alidata/log/web/chat.log','client:'.$req['fd'].' uid:'.$uid."\n",FILE_APPEND);
        if(empty($uid)){
            $uid = $req['msg']['from'];
        }
        if ($req)
        {
            switch($req['cmd'])
            {
                case 'getHistory':
                    $history = array('cmd'=> 'getHistory', 'history' => $this->store->getHistory());
                    if ($this->isCometClient($req['fd']))
                    {
                        return $req['fd'].json_encode($history);
                    }
                    //WebSocket客户端可以task中直接发送
                    else
                    {
                        $this->sendJson(intval($req['fd']), $history);
                    }
                    break;
                case 'addHistory':
                    if (empty($req['msg']))
                    {
                        $req['msg'] = '';
                    }
                    //$this->store->addHistory($req['fd'], $req['msg']);
                    (new Mysql())->addChat($uid, $req['msg']);
                    break;
                case 'addCall':
                    if (empty($req['msg']))
                    {
                        $req['msg'] = '';
                    }
                    //$this->store->addHistory($req['fd'], $req['msg']);
                    (new Mysql())->addCall($uid, $req['msg']);
                    break;
                case 'addNote':
                    (new Mysql())->addNote($uid, $req['msg']);
                    break;
                case 'setLive':
                    (new Mysql())->setLive($req['msg']);
                default:
                    break;
            }
        }
    }

    function onFinish($serv, $task_id, $data)
    {
        $this->send(substr($data, 0, 32), substr($data, 32));
    }

    /**
     * 获取在线列表
     */
    function cmd_getOnline($client_id, $msg)
    {
        $resMsg = array(
            'cmd' => 'getOnline',
        );
        $this->users = $users = $this->store->getOnlineUsers();
        //$info = $this->store->getUsers(array_slice($users, 0, 100));
        $resMsg['users'] = $users;
        //$resMsg['list'] = $info;
        $this->sendJson($client_id, $resMsg);
    }

    /**
     * 获取历史聊天记录
     */
    function cmd_getHistory($client_id, $msg)
    {
        $task['fd'] = $client_id;
        $task['cmd'] = 'getHistory';
        $task['offset'] = '0,100';
        //在task worker中会直接发送给客户端
        $this->getSwooleServer()->task(serialize($task), self::WORKER_HISTORY_ID);
    }

    /**
     * 登录
     * @param $client_id
     * @param $msg
     */
    function cmd_login($client_id, $msg)
    {
        $info['name'] = Filter::escape($msg['name']);
        $info['avatar'] = Filter::escape($msg['avatar']);

        //回复给登录用户
        $resMsg = array(
            'cmd' => 'login',
            'fd' => $client_id,
            'uid' => $msg['uid'],
            'name' => $info['name'],
            'avatar' => $info['avatar'],
            'is_teacher'    => (new Mysql())->isTeacher($msg['uid']),
        );
        file_put_contents('/alidata/log/web/login.log',$client_id.'--'.json_encode($resMsg)."\n",FILE_APPEND);

        //把会话存起来
        $this->users[$client_id] = $resMsg;

        $this->store->login($client_id, $resMsg);
        $this->sendJson($client_id, $resMsg);

        //广播给其它在线用户
        /*$resMsg['cmd'] = 'newUser';
        //将上线消息发送给所有人
        $this->broadcastJson($client_id, $resMsg);
        //用户登录消息
        $loginMsg = array(
            'cmd' => 'fromMsg',
            'from' => 0,
            'channal' => 0,
            'data' => $msg['name'] . "上线了",
        );
        $this->broadcastJson($client_id, $loginMsg);*/
    }

    /**
     * 发送信息请求
     */
    function cmd_message($client_id, $msg)
    {
        file_put_contents('/alidata/log/web/message.log','client:'.$client_id.' msg:'.json_encode($msg)."\n",FILE_APPEND);

        $resMsg = $msg;
        $resMsg['cmd'] = 'fromMsg';
        if($resMsg['reply_uid']){
            $resMsg['reply_time'] = date('Y-m-d H:i:s');
            $resMsg['create_time'] = date('Y-m-d H:i:s',strtotime($resMsg['create_time']));
        }else{
            $resMsg['create_time'] = date('Y-m-d H:i:s');
        }

        /*if (strlen($msg['data']) > self::MESSAGE_MAX_LEN)
        {
            $this->sendErrorMessage($client_id, 102, 'message max length is '.self::MESSAGE_MAX_LEN);
            return;
        }*/

        $resMsg['data'] = (new Word())->filterword($resMsg['data']);

        //表示私聊
        if (isset($resMsg['channal']) && $resMsg['channal'] == 1)
        {
            $info = $this->store->getUser($client_id);
            $resMsg['user'] = $info;
            $this->sendJson($msg['to'], $resMsg);
            //$this->store->addHistory($client_id, $msg['data']);
        }else
        {
            //群发
            $resMsg['user'] = $this->store->getUser($client_id);
            $resMsg['client_id'] = $client_id;

            file_put_contents('/alidata/log/web/message.log','client:'.$client_id.' resMsg:'.json_encode($resMsg)."\n",FILE_APPEND);
            $this->broadcastJson($client_id, $resMsg);
            $this->getSwooleServer()->task(serialize(array(
                'cmd' => 'addHistory',
                'msg' => $resMsg,
                'fd'  => $client_id,
            )), self::WORKER_HISTORY_ID);
        }
    }

    /**
     * @param $client_id
     * @param $msg
     * 喊单
     */
    function cmd_call($client_id, $msg){
        $info = $this->store->getUser($client_id);
        $msg['teacher'] = $info;
        $msg['create_time'] = date('Y-m-d H:i:s');
        if($msg['id']){
            $call = (new Mysql())->getCalldetail($msg['id']);
            $msg = array_merge($msg,$call);
        }
        file_put_contents('/alidata/log/web/call.log',json_encode($msg)."\n",FILE_APPEND);

        $this->broadcastJson($client_id, $msg);
        $this->getSwooleServer()->task(serialize(array(
            'cmd' => 'addCall',
            'msg' => $msg,
            'fd'  => $client_id,
        )), self::WORKER_HISTORY_ID);
    }

    /**
     * @param $client_id
     * @param $msg
     * 喊单
     */
    function cmd_live($client_id, $msg){
        file_put_contents('/alidata/log/web/status.log',json_encode($msg)."\n",FILE_APPEND);
        $this->broadcastJson($client_id, $msg);
        $this->getSwooleServer()->task(serialize(array(
            'cmd' => 'setLive',
            'msg' => $msg,
            'fd'  => $client_id,
        )), self::WORKER_HISTORY_ID);
    }

    /**
     * @param $client_id
     * @param $msg
     * 纸条
     */
    function cmd_note($client_id, $msg){
        if($msg['note_uid']){
            $info = $this->store->getUser($this->store->getUidClient($msg['note_uid']));
            $msg['note_user'] = $info;
        }
        $msg['create_time'] = date('Y-m-d H:i:s');
        $to_client = $this->store->getUidClient($msg['to']);
        //$msg['touser'] = $this->store->getUser($to_client);
        $this->sendJson($to_client, $msg);
        $this->getSwooleServer()->task(serialize(array(
            'cmd' => 'addNote',
            'msg' => $msg,
            'fd'  => $client_id,
        )), self::WORKER_HISTORY_ID);
    }



    /**
     * 接收到消息时
     * @see WSProtocol::onMessage()
     */
    function onMessage($client_id, $ws)
    {
        $this->log("onMessage #$client_id: " . $ws['message']);
        $msg = json_decode($ws['message'], true);
        if (empty($msg['cmd']))
        {
            $this->sendErrorMessage($client_id, 101, "invalid command");
            return;
        }
        $func = 'cmd_'.$msg['cmd'];
        if (method_exists($this, $func))
        {
            $this->$func($client_id, $msg);
        }
        else
        {
            $this->sendErrorMessage($client_id, 102, "command $func no support.");
            return;
        }
    }

    /**
     * 发送错误信息
    * @param $client_id
    * @param $code
    * @param $msg
     */
    function sendErrorMessage($client_id, $code, $msg)
    {
        $this->sendJson($client_id, array('cmd' => 'error', 'code' => $code, 'msg' => $msg));
    }

    /**
     * 发送JSON数据
     * @param $client_id
     * @param $array
     */
    function sendJson($client_id, $array)
    {
        $msg = json_encode($array);
        if ($this->send($client_id, $msg) === false)
        {
            $this->close($client_id);
        }
    }

    /**
     * 广播JSON数据
     * @param $client_id
     * @param $array
     */
    function broadcastJson($sesion_id, $array)
    {
        $msg = json_encode($array);
        $this->broadcast($sesion_id, $msg);
    }

    function broadcast($current_session_id, $msg)
    {
        foreach ($this->users as $client_id => $name)
        {
            if ($current_session_id != $client_id)
            {
                $this->send($client_id, $msg);
            }
        }
    }
}

